﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Windows;
using OpenLS.Spreadsheet.Collections;

namespace OpenLS.Spreadsheet.UI.Internals
{
    partial class ClusteredCellTextControl
    {
        internal class CellLayoutManager
        {
            internal readonly Worksheet Worksheet;
            private readonly Func<Cell, Rect> _rectFn;

            public CellLayoutManager(Worksheet worksheet, Func<Cell, Rect> rectFn)
            {
                Worksheet = worksheet;
                _rectFn = rectFn;
            }

            Dictionary<int, RowLayoutManager> rows = new Dictionary<int, RowLayoutManager>();

            internal HorizontalBounds GetMaxBoundsWithOverflow(Cell cell, Rect rect)
            {
                Contract.Requires(cell != null);
                Contract.Requires(cell.CanOverflow);
              //\\  Contract.Ensures(Contract.Result<double>() >= 0);
                Contract.Assert(Enumerable.Count(GetRowLayouts(cell)) > 0);
                Contract.Assert(Enumerable.Count(GetRowLayouts(cell)) > 0);
                var bounds =
                    Enumerable.ToList(Enumerable.Select(GetRowLayouts(cell), r => r.GetMaxBoundsWithOverFlow(cell)));
                return new HorizontalBounds(Enumerable.Max(bounds, b => b.Min), Enumerable.Min(bounds, b => b.Max));
//\\                return Enumerable.Min(GetRowLayouts(cell), r => r.GetMaxWidthWithOverFlow(cell));
            }
            IEnumerable <RowLayoutManager> GetRowLayouts (Cell cell)
            {
                Contract.Assert(cell != null);
                foreach (int i in GetRowIndices(cell))
                {
                    RowLayoutManager rowLayout;
                    if (!rows.TryGetValue(i, out rowLayout))
                    {
                        rowLayout = new RowLayoutManager(i, this.Worksheet, _rectFn);
                        rows.Add(i, rowLayout);
                    }
                    yield return rowLayout;
                }
            }
            static IEnumerable<int> GetRowIndices(Cell cell)
            {
                                for (int i = cell.Row.Index; i < cell.Row.Index + cell.RowSpan; i++)
                                    yield return i ;
            }

            class RowLayoutManager
            {
                readonly SortedList<int, Rect> _columnToRect = new SortedList<int, Rect>();
                public RowLayoutManager(int rowIndex, Worksheet worksheet, Func<Cell, Rect> rectFn)
                {
                    Row row = worksheet.Rows[rowIndex];
                    var rowCells = CellHelper.DistinctCells(row.GetCells());
                    Contract.Assert(
                        (Enumerable.Count(Enumerable.Distinct(rowCells)) == Enumerable.Count(rowCells)));
                    foreach (var cell in Enumerable.Where(rowCells, c => c.Value != null))
                    {
                        _columnToRect.Add(cell.Column.Index, rectFn(cell));
                    }
                    
                }
                public HorizontalBounds GetMaxBoundsWithOverFlow(Cell cell)
                {
                    Contract.Requires(cell != null);
                  //\\  Contract.Ensures(Contract.Result<double>() >= 0);
                    int columnIndex = cell.Column.Index;
                    int keyIndex = _columnToRect.IndexOfKey(columnIndex);
                    var h = cell.ActualHorizontalAlignment;
                    switch (h)
                    {
                        case HorizontalCellAlignment.Left:
                            {
                                if (keyIndex == _columnToRect.Count - 1)
                                    return new HorizontalBounds(0, double.MaxValue);

                                Rect thisRect = _columnToRect[_columnToRect.Keys[keyIndex]];
                                Rect nextRect = _columnToRect[_columnToRect.Keys[keyIndex + 1]];
                                return new HorizontalBounds(thisRect.Left, nextRect.Left);
                            }
                        case HorizontalCellAlignment.Right:
                            {
                                if (keyIndex == 0)
                                    return new HorizontalBounds(0, double.MaxValue);
                                Rect thisRect = _columnToRect[_columnToRect.Keys[keyIndex]];
                                Rect nextRect = _columnToRect[_columnToRect.Keys[keyIndex - 1]];
                                return new HorizontalBounds(nextRect.Right, thisRect.Right);
                            }
                            case HorizontalCellAlignment.Center:
                            case HorizontalCellAlignment.CenterContinuous:
                            {
                                Rect previousRect = new Rect(0, 0, 0, 0);
                                Rect nextRect = new Rect(double.MaxValue, double.MaxValue, 0, 0);
                                if (keyIndex > 0)
                                    previousRect = _columnToRect[_columnToRect.Keys[keyIndex - 1]];
                                if (keyIndex < _columnToRect.Count - 1)
                                    nextRect = _columnToRect[_columnToRect.Keys[keyIndex + 1]];
                                return new HorizontalBounds(previousRect.Right, nextRect.Left);
                            }

                        default:
                            // Contract.Assert(false);
                            return new HorizontalBounds(0, double.MaxValue);
                    }
                }
            }

            public void OnCellChanged(Cell cell)
            {
                foreach (var rowIndex in GetRowIndices(cell))
                {
                    this.rows.Remove(rowIndex);
                }
            }
        }

        private CellLayoutManager _layoutManager;
        private readonly Collections.HashSet<int> _dirtyOverflowRows = new Collections.HashSet<int>();

        internal CellLayoutManager LayoutManager
        {
            get
            {
                Contract.Ensures(Contract.Result<CellLayoutManager>() != null);
                Contract.Ensures(Contract.Result<CellLayoutManager>().Worksheet == Worksheet);
                if (_layoutManager == null)
                    _layoutManager = new CellLayoutManager(this.Worksheet, GetAbsoluteRect);
                return _layoutManager;
            }
        }
    }
}